#include "odbc/basic_statement.hpp"
#include "odbc/basic_connection.hpp"
#include "odbc/auto_commit_transaction_policy.hpp"
#include "odbc/prepared_stat_exec_policy.hpp"
#include "odbc/direct_stat_exec_policy.hpp"
#include "odbc/dataset_reader.hpp"
#include "odbc_test_defs.hpp"
#include "mysql_conn_fixture.hpp"
#include <boost/mpl/list.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <boost/test/test_case_template.hpp>
#include <boost/format.hpp>

typedef odbc::basic_connection< odbc::auto_commit_transaction_policy< true> > auto_commit_conn_3;
typedef odbc::basic_connection< odbc::auto_commit_transaction_policy< false> > no_auto_commit_conn_3;
typedef boost::mpl::list< auto_commit_conn_3, no_auto_commit_conn_3> connection_types;

namespace
{

using namespace odbc;

BOOST_AUTO_TEST_CASE_TEMPLATE( test_initialization, Connection, connection_types)
{
	mysql_conn_fixture< Connection> f;
	ODBCTEST_CHECK_NOTHROW(
			(basic_statement< prepared_stat_exec_policy>( f.connection_, "SELECT * FROM dbtype_test_data")));
}

// ---------------------------------------------------------------------------------------------------------------------

/// \brief It tests the execution of a prepared statement with no parameters and no checking for results.
BOOST_AUTO_TEST_CASE_TEMPLATE( test_prepared_stat_exec_no_param_no_res_check, Connection, connection_types)
{
	mysql_conn_fixture< Connection> f;
	basic_statement< prepared_stat_exec_policy> stat( f.connection_, "SELECT * FROM dbtype_test_data");
	ODBCTEST_CHECK_NOTHROW( stat.execute());
}

// ---------------------------------------------------------------------------------------------------------------------

/// \brief It tests the direct execution of a statement with no parameters and no checking for results.
BOOST_AUTO_TEST_CASE_TEMPLATE( test_direct_stat_exec_no_param_no_res_check, Connection, connection_types)
{
	mysql_conn_fixture< Connection> f;
	basic_statement< direct_stat_exec_policy> stat( f.connection_, "SELECT * FROM dbtype_test_data");
	ODBCTEST_CHECK_NOTHROW( stat.execute());
}


// ---------------------------------------------------------------------------------------------------------------------

BOOST_AUTO_TEST_CASE_TEMPLATE( test_prepared_stat_exec_in_param_binding, Connection, connection_types)
{
	mysql_conn_fixture< Connection> f;

	#define ODBCTEST_CHECK_PARAM_BINDING( COLUMN_NAME, TYPE, EXPECTED_VALUE) \
		{ \
		BOOST_CHECKPOINT( std::string( "Testing prepared statement input parameter binding for type ") + #TYPE); \
		basic_statement< prepared_stat_exec_policy> stat( f.connection_, \
			std::string("SELECT ") + COLUMN_NAME + " FROM dbtype_test_data WHERE " + COLUMN_NAME + "=?"); \
		ODBCTEST_CHECK_NOTHROW( stat.add_parameter( static_cast< TYPE>( EXPECTED_VALUE))); \
		ODBCTEST_CHECK_NOTHROW( stat.execute()); \
		}

	ODBCTEST_CHECK_PARAM_BINDING( "BOOL_FIELD", bool, true);
	ODBCTEST_CHECK_PARAM_BINDING( "TINYINT_FIELD", unsigned char, 'A');
	ODBCTEST_CHECK_PARAM_BINDING( "TINYINT_FIELD", char, 'A');
	ODBCTEST_CHECK_PARAM_BINDING( "TINYINT_FIELD", signed char, 'A');
	ODBCTEST_CHECK_PARAM_BINDING( "SMALLINT_FIELD", short, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "SMALLINT_FIELD", unsigned short, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "SMALLINT_FIELD", signed short, -100);
	ODBCTEST_CHECK_PARAM_BINDING( "MEDIUMINT_FIELD", short, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "MEDIUMINT_FIELD", unsigned short, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "MEDIUMINT_FIELD", signed short, -100);
	ODBCTEST_CHECK_PARAM_BINDING( "INT_FIELD", int, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "INT_FIELD", unsigned int, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "INT_FIELD", signed int, -100);
	ODBCTEST_CHECK_PARAM_BINDING( "BIGINT_FIELD", long, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "BIGINT_FIELD", unsigned long, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "BIGINT_FIELD", signed long, -100);
	ODBCTEST_CHECK_PARAM_BINDING( "FLOAT_FIELD", float, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "DOUBLE_FIELD", double, 100);
	ODBCTEST_CHECK_PARAM_BINDING( "VARCHAR_FIELD", std::string, "Initial");
	using namespace boost::gregorian;
	using namespace boost::posix_time;
	ODBCTEST_CHECK_PARAM_BINDING( "DATETIME_FIELD", date, date( 2008, 10, 1));
	ODBCTEST_CHECK_PARAM_BINDING( "DATETIME_FIELD", ptime, ptime( date( 2008, 10, 1), hours(10) + minutes(20) + seconds(30)));

	// TODO: Test for vector< unsigned char> (BLOB)

	#undef ODBCTEST_CHECK_PARAM_BINDING
}


template< class Connection, class T>
void test_query_field( const T& expected_val, const std::string& field_name)
{
	mysql_conn_dataset_fixture< Connection> f;

	typedef T value_type;
	typedef basic_statement< prepared_stat_exec_policy> tested_stat_type;
	typedef boost::tuples::tuple< value_type> record_type;
	typedef dataset_reader< record_type, tested_stat_type> tested_dataset_reader;

	tested_stat_type insert_stat( f.connection_, (boost::format("INSERT INTO dbtype_test_data( %1%) VALUES( ?) ") % field_name).str());
	insert_stat.add_parameter( expected_val);
	insert_stat.execute();

	BOOST_CHECKPOINT( "Querying the record");
	tested_stat_type query_stat( f.connection_, (boost::format( "SELECT %1% from dbtype_test_data WHERE %1% = ?") % field_name).str());
	query_stat.add_parameter( expected_val);
	query_stat.execute();
	tested_dataset_reader ds_reader( query_stat);
	typename tested_dataset_reader::const_iterator i = ds_reader.begin();
	BOOST_CHECK( i != ds_reader.end());
	boost::any value = (*i)[0];
	BOOST_CHECK( !value.empty());
	BOOST_CHECK_EQUAL( expected_val, boost::any_cast< value_type>( value));
	++i;
	BOOST_CHECK( i == ds_reader.end());
}

BOOST_AUTO_TEST_CASE_TEMPLATE( test_prepared_stat_col_fetching, Connection, connection_types)
{
	#define ODBCTEST_TEST_CASE( VAL, FIELD_NAME, TYPE_NAME) \
		BOOST_CHECKPOINT( std::string("Testing prepared statement columnm fetching for type ") + #TYPE_NAME); \
		test_query_field<Connection>( static_cast< TYPE_NAME>( VAL), FIELD_NAME);

	ODBCTEST_TEST_CASE( 'A', "TINYINT_FIELD", char);
	ODBCTEST_TEST_CASE( 'A', "TINYINT_FIELD", unsigned char);

	#undef ODBCTEST_TEST_CASE
}

} // namespace
